package com.rk.jarjuggler.action;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;

import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.DataConstants;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.module.ModifiableModuleModel;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ModifiableRootModel;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.roots.OrderRootType;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.roots.libraries.LibraryTable;
import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.vfs.JarFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.util.concurrency.SwingWorker;
import com.rk.jarjuggler.gui.LibTypePanel;
import com.rk.jarjuggler.gui.TreeTablePanel;
import com.rk.jarjuggler.model.LibNode;
import com.rk.jarjuggler.service.HttpUtil;

public class AddLibraryDialogAction extends DialogWrapper {

    public static final String JAR_DIR = "lib";
    public static final String SRC_DIR = "srclib";
    public static final String JAVADOC_DIR = "javadoc";

    private LibTypePanel libTypePanel;
    private AnActionEvent anAction;
    private static final String JAR_JUGGLER = "Jar Juggler";

    public AddLibraryDialogAction(Project project, AnActionEvent anAction) {
        super(project, false);
        this.anAction = anAction;
        init();
        setTitle(JAR_JUGGLER);
    }

    protected JComponent createNorthPanel() {
        return new JLabel("Select a library to add to the project:");
    }

    protected JComponent createCenterPanel() {
        TreeTablePanel treeTablePanel = new TreeTablePanel();
        treeTablePanel.setAddAction(new AddAction(treeTablePanel));

        JPanel container = new JPanel(new BorderLayout());
        container.add(treeTablePanel, BorderLayout.CENTER);
        libTypePanel = new LibTypePanel();
        container.add(libTypePanel, BorderLayout.SOUTH);

        return container;
    }

    protected Action[] createActions() {
      return new Action[]{getOKAction()};
    }

    protected boolean isModuleLibSelected() {
        return libTypePanel.isModuleLibSelected();
    }


    class AddAction implements ActionListener {
        private TreeTablePanel panel;

        public AddAction(TreeTablePanel panel) {
            this.panel = panel;
        }


        public void actionPerformed(ActionEvent actionEvent) {
            java.util.List<LibNode> libNodes = panel.getSelectedPackages();

            Project project = (Project) anAction.getDataContext().getData(DataConstants.PROJECT);
            String projectFolder = getProjectFilePath(project);

            for (LibNode libNode : libNodes) {
                // check to see if the user would like this lib
                if (Messages.showOkCancelDialog(project, "Add " + libNode.getJarUrl() + " ?", JAR_JUGGLER, Messages.getQuestionIcon()) == 1) {
                    continue;
                }

                if (doesLibExist(project, libNode)) {
                    Messages.showWarningDialog(project, "Skipping as this library already exists.", JAR_JUGGLER);
                    continue;
                }

                processNode(libNode, projectFolder, project);
            }
        }

        private boolean doesLibExist(Project project, LibNode libNode){
            String libName = getLibName(libNode);
            if (isModuleLibSelected()) {
                // Getting our Module:
                ModifiableModuleModel modifiableModel = ModuleManager.getInstance(project).getModifiableModel();
                Module theModule = modifiableModel.getModules()[0];

                // Getting Module Root Model:
                ModuleRootManager rootManager = ModuleRootManager.getInstance(theModule);
                ModifiableRootModel rootModel = rootManager.getModifiableModel();

                // Creating the new Library, adding its jar:
                Library myLibrary = rootModel.getModuleLibraryTable().getLibraryByName(libName);
                return myLibrary != null;

            } else {
                LibraryTable table = LibraryTablesRegistrar.getInstance().getLibraryTable(project);
                Library myLibrary = table.getLibraryByName(libName);
                return myLibrary != null;
            }
        }

        private void processNode(final LibNode libNode, final String projectFolder, final Project project) {
            SwingWorker worker = new SwingWorker() {
                public Object construct() {
                    try {
                        File[] files = new File[3];

                        File jarFile = null;
                        File srcFile = null;
                        File javadocFile = null;

                        if (libNode.hasJar()) {
                            jarFile = HttpUtil.downloadFileToProject(libNode.getJarUrl(), projectFolder, JAR_DIR);
                        }
                        if (libNode.hasSrc()) {
                            srcFile = HttpUtil.downloadFileToProject(libNode.getSrcUrl(), projectFolder, SRC_DIR);
                        }
                        if (libNode.hasJavadoc()) {
                            javadocFile = HttpUtil.downloadFileToProject(libNode.getJavadocUrl(), projectFolder, JAVADOC_DIR);
                        }

                        files[0] = jarFile;
                        files[1] = srcFile;
                        files[2] = javadocFile;
                        return files;
                    } catch (IOException e) {
                        e.printStackTrace();
                        throw new RuntimeException(e);
                    }
                }

                public void finished() {
                    File[] files = (File[]) getValue();

                    File jarFile = files[0];
                    File srcFile = files[1];
                    File javadocFile = files[2];

//                    Messages.showInfoMessage(project, "Finished downloading.  Adding to path", "Jar Juggler");
                    ApplicationManager.getApplication().runWriteAction(new WriteLibrary(isModuleLibSelected(), libNode, project, jarFile, srcFile, javadocFile));
                }

                public void onThrowable() {
                    Messages.showErrorDialog(project, "There was a problem downloading the files.", JAR_JUGGLER);
                }
            };
            worker.start();
        }

        private String getProjectFilePath(Project theProject) {
            String projectFilePath = theProject.getProjectFilePath();
            return projectFilePath.substring(0, projectFilePath.lastIndexOf(File.separator));
        }
    }

    class WriteLibrary implements Runnable {
        private boolean addAsModuleLib;
        private LibNode libNode;
        private Project project;
        private File jarFile;
        private File srcFile;
        private File javadocFile;


        public WriteLibrary(boolean addAsModuleLib, LibNode libNode, Project theProject, File jarFile, File srcFile, File javadocFile) {
            this.addAsModuleLib = addAsModuleLib;
            this.libNode = libNode;
            this.project = theProject;
            this.jarFile = jarFile;
            this.srcFile = srcFile;
            this.javadocFile = javadocFile;
        }

        public void run() {
            VirtualFileManager virtualFileManager = VirtualFileManager.getInstance();
            virtualFileManager.refresh(false);

            Library.ModifiableModel libraryModel;
            ModifiableRootModel rootModel = null;
            if (addAsModuleLib) {
                // Getting our Module:
                ModifiableModuleModel modifiableModel = ModuleManager.getInstance(project).getModifiableModel();
                Module theModule = modifiableModel.getModules()[0];

                // Getting Module Root Model:
                ModuleRootManager rootManager = ModuleRootManager.getInstance(theModule);
                rootModel = rootManager.getModifiableModel();

                // Creating the new Library, adding its jar:
                Library myLibrary = rootModel.getModuleLibraryTable().createLibrary(getLibName(libNode));
                libraryModel = myLibrary.getModifiableModel();
            } else {
                LibraryTable table = LibraryTablesRegistrar.getInstance().getLibraryTable(project);
                Library myLibrary = table.createLibrary();
                libraryModel = myLibrary.getModifiableModel();
                String libName = getLibName(libNode);
                libraryModel.setName(libName);
            }

            if (libNode.hasJar()) {
                String jarUrl = VirtualFileManager.constructUrl(JarFileSystem.PROTOCOL, HttpUtil.slashify(jarFile.getAbsolutePath()) + JarFileSystem.JAR_SEPARATOR);
                VirtualFile jarVirtualFile = virtualFileManager.findFileByUrl(jarUrl);
                libraryModel.addRoot(jarVirtualFile, OrderRootType.CLASSES);
            }
            if (libNode.hasSrc()) {
                String srcUrl = VirtualFileManager.constructUrl(JarFileSystem.PROTOCOL, HttpUtil.slashify(srcFile.getAbsolutePath()) + JarFileSystem.JAR_SEPARATOR);
                VirtualFile srcVirtualFile = virtualFileManager.findFileByUrl(srcUrl);
                libraryModel.addRoot(srcVirtualFile, OrderRootType.SOURCES);
            }
            if (libNode.hasJavadoc()) {
                String javadocUrl = VirtualFileManager.constructUrl(JarFileSystem.PROTOCOL, HttpUtil.slashify(javadocFile.getAbsolutePath()) + JarFileSystem.JAR_SEPARATOR);
                VirtualFile javadocVirtualFile = virtualFileManager.findFileByUrl(javadocUrl);
                libraryModel.addRoot(javadocVirtualFile, OrderRootType.JAVADOC);
            }

            libraryModel.commit();
            if (addAsModuleLib) {
                rootModel.commit();
            }
        }

    }

    private String getLibName(LibNode libNode) {
        return HttpUtil.getPrefix(HttpUtil.getFileName(libNode.getJarUrl()));
    }
}
